# -*- coding: utf-8 -*-

"""

@author: dongjiong

@contact: dongjiong@100.me

@file: balance_smooth_model.py

@time: 2024/6/05 2:55 PM

@desc:

"""
import pandas as pd
import warnings
import logging
from pyspark.sql import SparkSession
import numpy as np
from pyscipopt import Model, quicksum
import requests
import random

random.seed(14)


class IPDistributionSmoothModel:
    def __init__(self, u, TA, N, remainI, maxS):
        self.target_data = '2024-06-01'  # 日期
        self.u = u  # 调拨单位
        self.TA = TA  # 调拨总量
        self.N = N  # 门店数量
        self.remainI = remainI  # 数组，每个门店的剩余库存
        self.maxS = maxS  # 数组，每个门店的最大库存

    def solve(self):
        model = Model('Distribution Smooth Model')
        model.hideOutput(True)  # 不显示求解过程

        # x[i] 表示分拨到每个门店的单位分拨量
        x = []
        # t[i] 表示每个门店周转率和平均库存周转率的差值
        t = []
        for i in range(self.N):
            x.append(model.addVar(lb=0,
                                  vtype='I',
                                  name='x_' + 'i'))
            t.append(model.addVar(lb=0,
                                  vtype='C',
                                  name='x_' + 'i'))

        # z 表示最大周转率与最小周转率的差值
        z = model.addVar(lb=0, vtype='C', name='z')

        # z 约束条件
        for i in range(self.N):
            for j in range(i + 1, self.N):
                model.addCons(z >= ((self.remainI[i] + self.u * x[i]) / self.maxS[i])
                              - ((self.remainI[j] + self.u * x[j]) / self.maxS[j]))

                model.addCons(z >= ((self.remainI[j] + self.u * x[j]) / self.maxS[j])
                              - ((self.remainI[i] + self.u * x[i]) / self.maxS[i]))

        # t 约束条件
        for i in range(self.N):
            model.addCons(t[i] >= quicksum(((self.remainI[j] + self.u * x[j]) / self.maxS[j]) for j in range(self.N))
                          / self.N
                          - (self.remainI[i] + self.u * x[i]) / self.maxS[i])

            model.addCons(t[i] >= (self.remainI[i] + self.u * x[i]) / self.maxS[i] -
                          quicksum(((self.remainI[j] + self.u * x[j]) / self.maxS[j]) for j in range(self.N))
                          / self.N)

        # 调拨总量平衡约束
        model.addCons(quicksum((self.u * x[i]) for i in range(self.N)) == self.TA)

        # 优化目标，各个门店周转率相近
        target = sum(t) + z * self.N
        model.setObjective(target, 'minimize')

        # 求解配置
        gap = 0.05
        time_limit = 300
        model.setRealParam('limits/gap', gap)
        model.setRealParam('limits/time', time_limit)
        model.optimize()

        if model.getStatus() != 'infeasible':
            sum_t = model.getVal(sum(t))
            print('sum_turnover_rate = ' + str(sum_t))
            z = model.getVal(z)
            print('max_turnover_rate = ' + str(z))
            x_result = [0 for _ in range(self.N)]
            for i in range(self.N):
                x_result[i] = model.getVal(x[i])

            turnover_rate_n = [0 for _ in range(self.N)]
            for i in range(self.N):
                turnover_rate_n[i] = (self.remainI[i] + x_result[i]) / self.maxS[i]

            return x_result, turnover_rate_n
        else:
            print('!!!! infeasible !!!!')


def local_test():
    remainInventory_list = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17]
    maxStock_list = [3.2, 3.4, 3.5, 3.7, 3.3, 3.6, 3.5, 3.6, 3.4, 3.2, 3.1]
    N = len(maxStock_list)
    total_distribution = 5
    unit = 1
    model = IPDistributionSmoothModel(u=unit,
                                      TA=total_distribution,
                                      N=N,
                                      remainI=remainInventory_list,
                                      maxS=maxStock_list)
    allocation, min_turnover = model.solve()
    print(allocation)
    print(min_turnover)


def dj_tset():
    N = 200
    remainInventory_list = [random.randint(10, 100) for _ in range(N)]
    maxStock_list = [random.uniform(5, 20) for _ in range(N)]

    total_distribution = 12000
    unit = 2
    model = IPDistributionSmoothModel(u=unit,
                                      TA=total_distribution,
                                      N=N,
                                      remainI=remainInventory_list,
                                      maxS=maxStock_list)
    allocation, min_turnover = model.solve()
    print(allocation)
    print(min_turnover)


if __name__ == '__main__':
    dj_tset()
    # print('')
    # local_test()
